<template>
  <master :title="title">

    <!-- vis关系图基础展示 -->

    <el-dialog title="学生知识掌握图谱" :visible.sync="dialogVisible" width="90%" :before-close="destroyNetwork">
      <span>
        <!-- vis关系图基础展示 -->
        <div>
          <!--width,height 画布的宽度，高度。 可以是百分比或像素，一般在dom元素上设置 -->
          <div id="network_id" class="network" style="width:100%; height:60vh;border:1px solid #ccc;"></div>
        </div>
      </span>
      <span slot="footer" class="dialog-footer">
        <!-- <el-button @click="dialogVisible = false">取 消</el-button> -->
        <el-button type="primary" @click="dialogVisible = false;destroyNetwork()">确 定</el-button>
      </span>
    </el-dialog>

  </master>
</template>
<script type="text/javascript">
  (function (G) {
    var _this = null;
    G.vue({
      "usingComponents": {///引入组件
        "master": "/components/Master/Master",///母版组件
        "modal": "/components/Modal/Modal"
      },
      "enablePullDownRefresh": true
    }, G.modelList({///配置listpage组件
      modelName: 'sp_students',///entity名称
      title: '题库列表',
      listPage(list, query) {///query为前一个页面传过来的参数
        let type = query.type
        list.getUrl = '/api/model/sp_students';///列表请求API
        if (query.type) list.getUrl += '&type=' + query.type
        list.searchKey = 'name'///搜索字段，来源于列表数据模型
      },
      modeleditquery(edit, event, query) {///编辑页面 edit对象，event事件，query前一个页面传过来的参数
        if (query.type) {
          edit.values.type = query.type
          edit.models.type.type = ''
        }
      }
    }, {
      data: {
        /* 11111111111111111111vis知识图谱1111111111111111111111 */
        nodes: [],
        edges: [],
        // network:null,
        container: null,
        //   节点数组
        nodesArray: [
          {
            id: 0,
            name: "小明",
            group: "1",
          },
          {
            id: 1,
            name: "大立",
            group: "1",
          },
          {
            id: 2,
            name: "珊珊",
            group: "1",
          },
          {
            id: 3,
            name: "旺旺",
            group: "animal",
          },
          {
            id: 4,
            name: "咕咕",
            group: "animal",
          },
        ],
        //   关系线数组
        edgesArray: [
          { id: "e1", from: 0, to: 3, label: "宠物" },
          { id: "e2", from: 1, to: 4, label: "宠物" },
          { id: "e3", from: 0, to: 1, label: "好朋友" },
          { id: "e4", from: 1, to: 2, label: "好朋友" },
        ],

        options: {},
        datas: {},
        dialogVisible: true,
        /* 11111111111111111111vis知识图谱1111111111111111111111 */
      },
      methods: {
        /* 11111111111111111111111111111111111vis知识图谱11111111111111111111111111111111111111111111 */
        destroyNetwork(done) {
          if (this.network) {
            // console.log("销毁网络",this.network);

            this.network.destroy();
            // console.log("销毁网络",this.network);

            this.network = null;
            // console.log("销毁网络",this.network);
            console.log("销毁网络", done);


            if (done) {
              done(); // 必须调用，才能关闭对话框
            }
            G.$go(`/pages/course/list`);
          }
        },
        // 初始化network
        init() {
          this.$nextTick(() => {
            // 使用 $nextTick 方法，它会在下次 DOM 更新循环结束之后执行回调函数，
            // 确保相关的 DOM 操作在元素渲染完成后执行，常用于依赖于 DOM 状态更新后的操作
            //1.创建一个nodes对象，用于存储网络拓扑图中的节点数据
            this.nodes = new vis.DataSet([]);
            //2.创建一个edges对象，用于存储网络拓扑图中的边（连线）数据
            this.edges = new vis.DataSet([]);
            // 3.创建一个网络拓扑图
            // 通过 document.getElementById 方法获取页面中 id 为 "network_id" 的 DOM 元素，
            // 这个元素将作为网络拓扑图的容器
            this.container = document.getElementById("network_id");
            // 4.创建datas对象
            // 用于后续初始化网络拓扑图时传递数据
            this.datas = {
              nodes: this.nodes,
              edges: this.edges,
            };
            // 5.全局配置
            this.options = {
              autoResize: true, //网络将自动检测其容器的大小调整，并相应地重绘自身，若为true，则会自动调整大小
              locale: "cn", //语言设置：工具栏显示中文
              //设置语言
              locales: {
                cn: {
                  //工具栏中文翻译
                  edit: "编辑",
                  del: "删除当前节点或关系",
                  back: "返回",
                  addNode: "添加节点",
                  addEdge: "添加连线",
                  editNode: "编辑节点",
                  editEdge: "编辑连线",
                  addDescription: "点击空白处可添加节点",
                  edgeDescription: "点击某个节点拖拽连线可连接另一个节点",
                  editEdgeDescription: "可拖拽连线改变关系",
                  createEdgeError: "无法将边连接到集群",
                  deleteClusterError: "无法删除集群.",
                  editClusterError: "无法编辑群集'",
                },
              },
              //该配置项主要用来生成一个可视化的配置器
              // configure 配置项相关，目前 enabled 设置为 false 表示不启用可视化配置器，
              // filter 规定了配置器可配置的对象（节点和边），container 指定配置器所在的容器（即网络拓扑图的容器），
              // showButton 为 true 表示显示相关操作按钮（不过目前由于 enabled 为 false 暂不生效）
              configure: {
                enabled: false,
                filter: "nodes,edges",
                container: this.container,
                showButton: true,
              },
              // 组模块
              groups: {
                // useDefaultGroups 为 true 表示使用默认的分组设置（具体默认设置可能由库本身决定），
                useDefaultGroups: true,
                myGroupId: {},
                1: {
                  shape: "circle",
                  color: { background: "#78e08f" },
                  font: { color: "white" },
                },
                // animal: {
                //   shape: "dot",
                // color: { background: "#7ed6df" },
                // },
                2: {
                  shape: "circle",
                  color: { background: "#69c4ff" },
                  font: { color: "white" },
                },
                3: {
                  shape: "circle",
                  color: { background: "#a7d3f5" },
                  font: { color: "white" },
                },
                4: {
                  shape: "circle",
                  color: { background: "#f9c3d3" },
                  font: { color: "white" },
                },
                5: {
                  shape: "circle",
                  color: { background: "#ff8c94" },
                  font: { color: "white" },
                },
                6: {
                  shape: "circle",
                  color: { background: "#ff6b6b" },
                  font: { color: "white" },
                }
              },

              // 设置节点样式
              nodes: {
                shape: "ellipse", //节点的外观。为circle时label显示在节点内，为dot时label显示在节点下方
                // size: 30, //节点的大小，
                shadow: false, //如果为true，则节点使用默认设置投射阴影。
                font: {
                  //字体配置
                  size: 20,
                  color: "rgb(117, 218, 167)",
                  align: "center",
                },
                color: {
                  border: "transparent", //节点边框颜色
                  background: "#fd91b7", //节点背景颜色
                  highlight: {
                    //节点选中时状态颜色
                    border: "rgb(117, 218, 167)",
                    background: "rgb(117, 218, 167)",
                  },
                  hover: {
                    //节点鼠标滑过时状态颜色
                    border: "#dff9fb",
                    background: "#88dab1",
                  },
                },
                margin: 5, //当形状设置为box、circle、database、icon、text；label的边距
                widthConstraint: 100, //设置数字，将节点的最小和最大宽度设为该值,当值设为很小的时候，label会换行，节点会保持一个最小值，里边的内容会换行
                borderWidth: 1, //节点边框宽度，单位为px
                borderWidthSelected: 3, //节点被选中时边框的宽度，单位为px
                labelHighlightBold: false, //确定选择节点时标签是否变为粗体
              },
              // 边线配置
              // 对网络拓扑图中边（连线）的样式、长度、颜色、字体以及连线的平滑度、箭头等属性进行配置，
              // 例如控制连线宽度、不同交互状态下的颜色变化、是否显示阴影、连线是否平滑（曲线还是直线）以及箭头指向等
              edges: {
                width: 1,
                length: 200,// 原始的连线长度（后续会根据不同组间的设置覆盖）
                color: {
                  color: "#848499",
                  highlight: "rgb(117, 218, 167)",
                  hover: "#88dab1",
                  inherit: "from",
                  opacity: 1.0,
                },
                font: {
                  color: "#343434",
                  size: 18, // px
                  face: "arial",
                  background: "none",
                  strokeWidth: 2, // px
                  strokeColor: "#ffffff",
                  align: "horizontal",
                  multi: false,
                  vadjust: 0,
                  bold: {
                    color: "#343434",
                    size: 14, // px
                    face: "arial",
                    vadjust: 0,
                    mod: "bold",
                  },
                  ital: {
                    color: "#343434",
                    size: 14, // px
                    face: "arial",
                    vadjust: 0,
                    mod: "italic",
                  },
                  boldital: {
                    color: "#343434",
                    size: 14, // px
                    face: "arial",
                    vadjust: 0,
                    mod: "bold italic",
                  },
                  mono: {
                    color: "#343434",
                    size: 15, // px
                    face: "courier new",
                    vadjust: 2,
                    mod: "",
                  },
                },
                shadow: false,
                smooth: {
                  //设置两个节点之前的连线的状态
                  enabled: true, //默认是true，设置为false之后，两个节点之前的连线始终为直线，不会出现贝塞尔曲线
                },
                arrows: { to: true }, //箭头指向to
              },
              // 布局
              layout: {
                randomSeed: 1, //配置每次生成的节点位置都一样，参数为数字1、2等
                // hierarchical: {
                //   direction: "DU", //UD:上下 DU:下上 LR:左右 RL:右左
                //   sortMethod: "directed",
                // }, //层级结构显示}
              },
              //物理引擎-计算节点之前斥力，进行自动排列的属性
              physics: {
                enabled: true, //默认是true，设置为false后，节点将不会自动改变，拖动谁谁动。不影响其他的节点
                // enabled属性用于控制物理引擎是否启用。当设置为true时，物理引擎生效，节点之间会基于相关物理规则（如斥力等）自动排列；
                // 若设置为false，则节点失去自动排列的能力，只有手动拖动某个节点时，该节点才会移动，不会影响其他未被拖动的节点位置。

                barnesHut: {
                  gravitationalConstant: -12000,
                  // 引力常量，用于控制节点之间的引力大小，值为负数表示是吸引力的作用方向，具体数值大小影响吸引力的强度，
                  // 该值与其他参数共同作用来决定节点在布局中的相对位置关系。
                  centralGravity: 0.3,
                  // 中心引力系数，决定了整体布局向中心聚集的趋势强度，取值范围通常在0到1之间，
                  // 该值越大，节点越倾向于向整个布局的中心位置聚集。
                  springLength: 120,
                  // 弹性长度，可理解为节点之间通过类似弹簧连接时的原始长度，当节点间距离偏离这个长度时，会基于弹簧系数产生相应的作用力，
                  // 用于调整节点之间的间距，使其在一定程度上保持相对合理的分布。
                  springConstant: 0.04,
                  // 弹簧系数，决定了节点之间类似弹簧连接时的弹性强度，即当节点间距偏离弹簧长度时，该系数决定了节点受到的拉力或推力大小，
                  // 与弹簧长度配合影响节点的动态排列效果。
                  damping: 0.09,
                  // 阻尼系数，用于模拟物理世界中的摩擦力等阻尼效果，控制节点在运动过程中的减速情况，避免节点无限制地来回摆动或运动，
                  // 使节点的运动能更自然、平稳地趋于稳定状态。
                  avoidOverlap: 0,
                  // 避免重叠系数（具体含义可能因库的实现有所差异），通常用于控制节点之间尽量不出现重叠的程度，
                  // 值越大越倾向于严格避免节点重叠，这里设置为0可能表示不进行相关的强力避免重叠处理或采用默认的较弱处理方式。
                },
              },
              //用于所有用户与网络的交互。处理鼠标和触摸事件以及导航按钮和弹出窗口
              interaction: {
                hover: true,
                // 当鼠标悬停（hover）在节点或连线上时，触发相应的交互效果（比如改变样式等），这里设置为true表示启用该交互功能。
                dragNodes: true, //是否能拖动节点
                // 设置为true表示允许用户通过鼠标拖动节点来改变其在网络拓扑图中的位置，若为false则禁止拖动节点操作。
                dragView: true, //是否能拖动画布
                // 如果设置为true，用户可以通过鼠标拖动整个网络拓扑图的视图（画布）来进行平移查看不同区域，若为false则不能进行此操作。
                hover: true, //鼠标移过后加粗该节点和连接线
                // 再次出现的hover属性（这里可能存在代码重复或可优化的情况，但功能上同样是控制鼠标悬停交互效果），
                // 设置为true表示当鼠标悬停在节点或连线上时，相应的节点和连接线会以加粗等方式突出显示，便于用户识别和查看。
                multiselect: true, //按 ctrl 多选
                // 设置为true后，用户可以通过按住Ctrl键（在常见的操作系统交互习惯下）来实现多选节点或其他元素的操作，方便批量处理。
                selectable: true, //是否可以点击选择
                // 若为true，则用户可以通过鼠标点击来选中节点等元素，选中后可进行后续相关的操作（比如查看属性、删除等），若为false则禁止点击选择操作。
                selectConnectedEdges: true, //选择节点后是否显示连接线
                // 当用户选中某个节点时，设置为true会自动显示与该节点相连接的所有连接线，便于查看节点之间的关联关系，若为false则不显示相关连接线。
                hoverConnectedEdges: true, //鼠标滑动节点后是否显示连接线
                // 当鼠标悬停在某个节点上时，设置为true会显示与该节点相关的连接线，方便用户直观了解节点的连接情况，若为false则不进行此显示。
                hideEdgesOnDrag: true, //拖动视图时不会绘制边
                // 在拖动画布（视图）的过程中，设置为true表示暂时隐藏连接线，这样可以减少拖动过程中的视觉干扰，使视图拖动更流畅，
                // 拖动结束后连接线会恢复显示，若为false则拖动时连接线依然正常显示。
                zoomView: true, //是否能缩放画布
                // 设置为true表示允许用户通过鼠标滚轮等方式对整个网络拓扑图的视图进行缩放操作，方便查看不同比例下的布局情况，若为false则禁止缩放操作。
              },
            };

            // 6.初始化网络拓扑图
            this.network = new vis.Network(this.container, this.datas, this.options);

            this.network.moveTo({ scale: 0.2 });
          });

        },
        //扩展节点 增加nodes和edges集合参数
        addNetworkParams(param) {
          let colorList = ["#78e08f", "#69c4ff", "#a7d3f5", "#f9c3d3", "#ff8c94", "#ff6b6b"]
          //添加节点
          // for (var i = 0; i < param.nodes.length; i++) {
          //   var node = param.nodes[i];
          //   // console.log("node.size", node.size);
          //   // console.log("node.score", node.score);
          //   if (node.score == 0) {
          //     this.nodes.add({
          //       label: node.name,
          //       size: node.size,
          //       color: "#ccc",
          //       ...node,
          //     });
          //   } else if (node.score <= 60) {
          //     this.nodes.add({
          //       label: node.name,
          //       size: node.size,
          //       color: "#409EFF",
          //       ...node,
          //     });
          //   }else if (node.score <= 75) {
          //     this.nodes.add({
          //       label: node.name,
          //       size: node.size,
          //       color: "#78e08f",
          //       ...node,
          //     });
          //   } else if (node.score <= 90) {
          //     this.nodes.add({
          //       label: node.name,
          //       size: node.size,
          //       color: "#ff8f00",
          //       ...node,
          //     });
          //   } else if (node.score <= 100) {
          //     this.nodes.add({
          //       label: node.name,
          //       size: node.size,
          //       color: "#ff6000",
          //       ...node,
          //     });
          //   } else {
          //     this.nodes.add({
          //       label: node.name,
          //       size: node.size,
          //       color: "#ff6b6b",
          //       ...node,
          //     });
          //   }
          //   // console.log("nodes=", this.nodes.get());
          // }


          // 添加节点
          for (var i = 0; i < param.nodes.length; i++) {
            var node = param.nodes[i];

            let color = "#ccc"; // 默认颜色

            if (node.score == 0) {
              color = "#ccc";
            } else if (node.score > 0 && node.score <= 100) {
              // 根据分值动态生成绿色，分值越高绿色越深    红和蓝小于绿色，绿色大于60保证是绿色
              let greenValue = Math.floor(255 - (node.score - 60) * 4.75); // 分值范围映射到绿色分量(120到225)
              greenValue = Math.max(140, Math.min(greenValue, 225)); // 限制范围在140-225之间

              // 红色和蓝色分量设置为绿色分量的一半，确保颜色显绿色
              let redBlueValue = Math.floor(greenValue * 0.5);

              color = `rgb(${redBlueValue}, ${greenValue}, ${redBlueValue})`; // 动态绿色
            } else {
              color = "#ff6b6b"; // 超过100分的颜色
            }

            this.nodes.add({
              label: node.name,
              size: node.size,
              color: color,
              ...node,
            });
          }

          //添加关系
          for (var i = 0; i < param.edges.length; i++) {
            var edge = param.edges[i];
            this.edges.add({
              ...edge,
            });
          }
        },
        //获取数据
        async getNetList(StudentId, courseId) {
          // console.log("StudentId=", StudentId);
          const res = await G.get('/v1/students/chapter', { studentId: StudentId, courseId: courseId })
          console.log("res=", res);
          // 清空数组，避免重复添加
          this.nodesArray = [];
          this.edgesArray = [];
          // 遍历数据生成节点数组
          res.forEach(item => {
            //去掉5级和6级的子节点
            if (item.type != '5' && item.type != '6') {
              this.nodesArray.push({
                id: item.id,
                label: item.name,
                group: item.type,
                size: item.type == 1 ? 80 :
                  item.type == 0 ? 80 :
                    item.type == 2 ? 70 :
                      item.type == 3 ? 60 :
                        item.type == 4 ? 50 :
                          item.type == 5 ? 40 : 30, // 默认值
                score: item.score
              });
            }
          });

          // console.log("nodesArray=", this.nodesArray);

          // 遍历数据生成关系数组
          res.forEach(item => {
            //去掉5级和6级的子节点
            if (item.type != '5' && item.type != '6') {
              if (item.parent_id) { // 确保 parentId 存在
                this.edgesArray.push({
                  from: item.id,
                  to: item.parent_id,
                  // label: item.type // 可选
                });
              }
            }

          });
          // console.log("edgesArray=", this.edgesArray);

          var param = { nodes: this.nodesArray, edges: this.edgesArray };
          // console.log("param.nodes=", param.nodes);

          this.addNetworkParams(param);


        },
        /* 111111111111111111111111111111111111vis知识图谱1111111111111111111111111111111111111111111 */

      },
      mounted: function (query) {
        _this = this,
        _this.dialogVisible = true;
        _this.getNetList(query.SpStudentId, query.SpCourseId);  //学生id和课程id
        _this.init();
      }
    }));
  })(Y)
</script>
<style scoped>

</style>